#-*-coding:utf-8
import logging

_signatureDict = {
"Z" : ["GetBooleanField", "SetBooleanField"],
"B" : ["GetByteField", "SetByteField"],
"C" : ["GetCharField", "SetCharField"],
"S" : ["GetShortField", "SetShortField"],
"I" : ["GetIntField", "SetIntField"],
"J" : ["GetLongField", "SetLongField"],
"F" : ["GetFloatField", "SetFloatField"],
"D" : ["GetDoubleField", "SetDoubleField"],
}

def isObjectSignature(signature):
    """判断符号是不是Object类型的即非基本类型"""
    if signature not in _signatureDict:
        return True
    return False

def getGetAndSetMethods(signature):
    objMethod = ["GetObjectField", "SetObjectField"]
    if isObjectSignature(signature):
        return objMethod
    else:
        return _signatureDict[signature]


class OutPut():
    """根据输入的字段列表和字段的字典来输出相应的.h和.cpp文件"""

    _headTemplateString = """#ifndef _{self.CLASSNAME}_INCLUDED_H
#define _{self.CLASSNAME}_INCLUDED_H
#include <jni.h>
#include <string>


namespace JavaData
{{

class {self.classname}
{{
public:
    {self.fieldList}
}};

class {self.classname}Proxy
{{
public:
    {self.classname}Proxy() : {self.fieldIdInit}, clazz(0), g_clazzRef(0),isInitialized(false), env(NULL)
    {{}}
    ~{self.classname}Proxy() {{ if (env && clazz && g_clazzRef) env->DeleteGlobalRef(g_clazzRef); }}

    void translateFromJava(JNIEnv *env, jobject obj, {self.classname} &{self.instancename});

    jobject translateToJava(JNIEnv *env, const {self.classname} &{self.instancename});

    static void staticTranslateFromJava(JNIEnv *env, jobject obj, {self.classname} &{self.instancename})
    {{
        {self.classname}Proxy p;
        p.translateFromJava(env, obj, {self.instancename});
    }}

    static jobject staticTranslateToJava(JNIEnv *env, const {self.classname} &{self.instancename})
    {{
        {self.classname}Proxy p;
        return p.translateToJava(env, {self.instancename});
    }}

private:
    void Initialize(JNIEnv *env)
    {{
        if (env && !isInitialized)
        {{
            this->env = env;
            clazz = env->FindClass("{self.classpath}");
            g_clazzRef = env->NewGlobalRef(clazz);
            {self.getFieldId}
            isInitialized = true;
        }}
    }}
    jclass clazz;
    jobject g_clazzRef;
    {self.fieldIdDeclare}
    bool isInitialized;
    JNIEnv *env;
}};

}}

#endif //_{self.CLASSNAME}_INCLUDED_H
"""

    _implementTemplateString = """#include \"{self.headername}.h\"

namespace JavaData
{{

void {self.classname}Proxy::translateFromJava(JNIEnv *env, jobject obj, {self.classname} &{self.instancename})
{{
    if (env)
    {{
        Initialize(env);
        if (clazz)
        {{
            {self.getField}
        }}
    }}
}}

jobject {self.classname}Proxy::translateToJava(JNIEnv *env, const {self.classname} &{self.instancename})
{{
    if (env)
    {{
        Initialize(env);
        if (clazz)
        {{
            jobject obj = env->AllocObject(clazz);
            if (obj != NULL)
            {{
                {self.setField}
                return obj;
            }}
        }}
        return NULL;
    }}
    return NULL;
}}

}}
"""

    def __init__(self, fieldList, fieldMap, wholeClassName, outpath):
        name = wholeClassName.split('.')[-1]
        self.tabSpace = '    '
        self.CLASSNAME = name.upper()
        self.classname = name
        self.fieldList = ('\n'+self.tabSpace).join(fieldList)
        self.fieldIdInit = ",".join([field+"Id(0)" for field in fieldMap.keys()])
        self.instancename = name.lower()
        self.classpath = wholeClassName.replace('.','/')
        self.fieldIdList = [field+"Id" for field in fieldMap.keys()]
        self.fieldIdDeclare = ("\n"+self.tabSpace).join(["jfieldID "+field+"Id;" \
                                                 for field in fieldMap.keys()])
        self.headername = name.lower()
        self.getField = self._getGetFieldString(fieldMap)
        self.setField = self._getSetFieldString(fieldMap)
        self.getFieldId = self._getFieldIdString(fieldMap)
        self.outpath = outpath

    def _getFieldIdString(self, fieldMap):
        l = list()
        for k, v in fieldMap.iteritems():
            s = k +"Id" + " = env->GetFieldID(clazz, \"" + k + "\", \"" + v + "\");";
            l.append(s)
        return ("\n"+3*self.tabSpace).join(l)

    def _getGetFieldString(self, fieldMap):
        template = "{instancename}.{fieldName} = env->{getMethod}(obj, {fieldName}Id);"
        l = list()
        for k, v in fieldMap.iteritems():
            line = template.format(instancename=self.instancename, fieldName=k, \
                       getMethod=getGetAndSetMethods(v)[0])
            l.append(line)
        logging.debug(l)
        return ("\n"+3*self.tabSpace).join(l)

    def _getSetFieldString(self, fieldMap):
        template = "env->{setMethod}(obj, {fieldName}Id, {instancename}.{fieldName});"
        l = list()
        for k, v in fieldMap.iteritems():
            line = template.format(instancename=self.instancename, fieldName=k, \
                                   setMethod=getGetAndSetMethods(v)[1])
            l.append(line)
        logging.debug(l)
        return ("\n"+4*self.tabSpace).join(l)


    def output(self):
        """输出.h文件和.cpp文件"""

        headerStr = OutPut._headTemplateString.format(self=self)
        implementStr = OutPut._implementTemplateString.format(self=self)
        outFile = self.outpath + "\\" + self.headername + ".h"
        outCppFile = self.outpath + "\\" + self.headername + ".cpp"
        with open(outFile, "w+") as o:
            o.write(headerStr)

        with open(outCppFile, "w+") as o:
            o.write(implementStr)
